package sk.naive.talker.server;

import sk.naive.talker.*;
import sk.naive.talker.AdapterProxy;
import sk.naive.talker.message.Message;
import sk.naive.talker.util.*;
import sk.naive.talker.persistence.*;
import sk.naive.talker.callback.CallbackException;
import sk.naive.talker.command.CommandCallback;
import sk.naive.talker.adapter.AdapterUser;
import sk.naive.talker.props.*;

import java.util.*;
import java.rmi.RemoteException;

/**
 * Factory for user object creation.
 * <p>
 * Used in user proxy classes and user finder implementation (UserRegistry).
 * Initialized when talker is started - stored in talker object.
 *
 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
 * @version $Revision: 1.18 $ $Date: 2005/01/16 22:20:48 $
 */
public class UserFactory extends PersistentObjectFactory {
	public UserFactory(String propertiesBundleName) {
		super(new MutablePropertyConfiguration(propertiesBundleName));
	}

	public void addAdapterConfiguration(Set<String> userSettable, Set<String> userPersistent, Map<String, Property> props) {
		MutablePropertyConfiguration mutablePropertyConfiguration = (MutablePropertyConfiguration) propertyConfiguration;
		mutablePropertyConfiguration.addSettable(userSettable);
		mutablePropertyConfiguration.addPersistent(userPersistent);
		mutablePropertyConfiguration.mergeProperties(props);
	}

	public Property getProperty(String key) {
		return propertyConfiguration.getProperty(key);
	}

	public UserProxy createOnlineUser(Integer id, AdapterUser u, AdapterProxy a) throws RemoteException {
		return new OnlineUser(id, u, a);
	}

	public UserProxy createOfflineUser(Integer id) {
		return new OfflineUser(id);
	}

	protected int getType() {
		return DbLayer.OBJECT_TYPE_USER;
	}

	protected String getNamingProperty() {
		return User.UPROP_LOGIN;
	}

	protected PersistentObject persistentObjectInstance(Integer id) throws PersistenceException {
		throw new UnsupportedOperationException();
	}

	public PersistentObject create(String name) throws PersistenceException {
		throw new UnsupportedOperationException();
	}

	public PersistentObject getPersistentObjectByName(String name) throws PersistenceException {
		throw new UnsupportedOperationException();
	}

	public PropertyConfiguration getPropertyConfiguration() {
		throw new UnsupportedOperationException();
	}

	/**
	 * Online user proxy implementation.
	 *
	 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
	 * @version $Revision: 1.18 $ $Date: 2005/01/16 22:20:48 $
	 */
	private class OnlineUser extends UserProxy {
		private AdapterUser remoteUser;
		private AdapterProxy adapter;
		private Map commandCallbacks;

		private int callbackIdCounter;
		private static final int MAX_CALLBACKS = 5;

		public OnlineUser(Integer id, AdapterUser u, AdapterProxy a) {
			super(id, propertyConfiguration);
			remoteUser = u;
			adapter = a;
			commandCallbacks = new HashMap();
			callbackIdCounter = 0;
		}

		public void send(Message m) throws RemoteException {
			// TODO: filtering (treti system - napr. echo meno len pre wizov a tak)
			send(m.get(getLang()));
		}

		public void send(String s) throws RemoteException {
			remoteUser.send(s);
		}

		public AdapterProxy getAdapter() {
			return adapter;
		}

		public Object get(String key) throws PropertyStoreException {
			if (adapter.getUserProperties().contains(key)) {
				try {
					return remoteUser.get(key);
				} catch (RemoteException e) {
					throw new PropertyStoreException(e);
				}
			}
			return super.get(key);
		}

		public void setStoredValue(String key, String value) throws PropertyStoreException {
			super.setStoredValue(key, value);
			remoteSet(key, value);
		}

		public void set(String property, Object value) throws PropertyStoreException {
			super.set(property, value);
			remoteSet(property, value);
		}

		public void setNonpersistent(String property, Object value) throws PropertyStoreException {
			super.setNonpersistent(property, value);
			remoteSet(property, value);
		}

		private void remoteSet(String key, Object value) throws PropertyStoreException {
			if (adapter.getUserProperties().contains(key) || getPropertyConfiguration().getGlobal().contains(key)) {
				try {
					remoteUser.set(key, value);
				} catch (RemoteException e) {
					throw new PropertyStoreException(e);
				}
			}
		}

		public String setupCommandCallback(CommandCallback callback) throws CallbackException {
			if (commandCallbacks.size() >= MAX_CALLBACKS) {
				throw new CallbackException("Maximum number of callbacks reached");
			}
			String cId = String.valueOf(callbackIdCounter++);
			commandCallbacks.put(cId, callback);
			return cId;
		}

		public CommandCallback useCommandCallback(String cId) {
			return (CommandCallback) commandCallbacks.remove(cId);
		}

		public AdapterUser getRemoteUser() {
			return remoteUser;
		}

		public boolean isOnline() {
			return true;
		}
	}

	/**
	 * Offline user proxy implementation.
	 *
	 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
	 * @version $Revision: 1.18 $ $Date: 2005/01/16 22:20:48 $
	 */
	private class OfflineUser extends UserProxy {
		public OfflineUser(Integer id) {
			super(id, propertyConfiguration);
		}

		public void send(Message m) throws RemoteException {
			throw new UnsupportedOperationException();
		}

		public void send(String s) throws RemoteException {
			throw new UnsupportedOperationException();
		}

		public AdapterProxy getAdapter() {
			throw new UnsupportedOperationException();
		}

		public String setupCommandCallback(CommandCallback callback) throws CallbackException {
			throw new UnsupportedOperationException();
		}

		public CommandCallback useCommandCallback(String cId) {
			throw new UnsupportedOperationException();
		}

		public AdapterUser getRemoteUser() {
			throw new UnsupportedOperationException();
		}

		public boolean isOnline() {
			return false;
		}
	}
}